package com.hup.utils.commons.start;

import com.hup.utils.commons.hardware.ScreenUtil;
import lombok.Getter;
import lombok.extern.log4j.Log4j2;

import javax.swing.AbstractButton;
import javax.swing.JFormattedTextField;
import javax.swing.JSlider;
import javax.swing.JSpinner;
import javax.swing.ToolTipManager;
import javax.swing.UIManager;
import javax.swing.plaf.FontUIResource;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Window;
import java.util.Enumeration;

/**
 * @author hugan
 * @date 2019/8/17
 */
@Log4j2
public class LookThemeUtil {

    private static final int DEFAULT_FONT_SIZE = 16;

    /**
     * 屏幕缩放比例[1.0,2.0],默认是[系统当前缩放比例]
     */
    @Getter
    private static float screenScale;

    static {
        Integer scale = ScreenUtil.getScreenScale();
        screenScale = scale == null ? 1 : scale / 100f;
    }

    /**
     * 更改缩放比例
     *
     * @apiNote 在这之前创建组件不会受影响!
     */
    private static void setScreenScale(float scale) {
        if (scale < 1 || scale > 2) {
            throw new IllegalArgumentException("scale范围是[1.0,2.0],取值=" + scale);
        }
        LookThemeUtil.screenScale = scale;
        initFontAndSize(getDefaultScaledFont());
    }

    /**
     * @param scale [1.0,2.0]
     */
    public static void initNormal(float scale) {
        try {
            UIManager.setLookAndFeel(Look.CURRENT_SYSTEM.className);
            setScreenScale(scale);
        } catch (Throwable e) {
            e.printStackTrace();
        }
    }

    /**
     * 初始化外观样式和全局字体
     *
     * @param look nullable,空时使用当前系统的主题
     */
    public static void initNormal(Look look) {
        try {
            if (look == null) look = Look.CURRENT_SYSTEM;

            UIManager.setLookAndFeel(look.className);
            initFontAndSize(getDefaultScaledFont());
        } catch (Throwable e) {
            e.printStackTrace();
        }
    }

    public static Font getDefaultScaledFont() {
        return new Font("宋体", Font.PLAIN, (int) (DEFAULT_FONT_SIZE * screenScale));
    }

    public static int getDefaultScaledFontSize() {
        return (int) (DEFAULT_FONT_SIZE * screenScale);
    }

    public static void initToolTip() {
        ToolTipManager.sharedInstance().setInitialDelay(300);//鼠标停留ms后开始展示tooltip
        ToolTipManager.sharedInstance().setDismissDelay(20000);//展示ms后隐藏
    }

    /**
     * ui主题样式
     */
    public enum Look {
        /**
         * 当前系统
         */
        CURRENT_SYSTEM(UIManager.getSystemLookAndFeelClassName()),
        /**
         * nimbus,以前项目用的,要修改字体才好看
         */
        NIMBUS("com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel"),
        /**
         * swing默认
         */
        DEFAULT("javax.swing.plaf.metal.MetalLookAndFeel"),
        /**
         * 如果是window系统,则是当前主题
         */
        WINDOWS("com.sun.java.swing.plaf.windows.WindowsLookAndFeel"),
        /**
         * 老式window主题
         */
        @Deprecated
        WINDOWS_OLD("com.sun.java.swing.plaf.windows.WindowsClassicLookAndFeel"),
        /**
         * motify异类
         */
        @Deprecated
        MOTIF("com.sun.java.swing.plaf.motif.MotifLookAndFeel"),
        /**
         * 跨平台,等同于DEFAULT
         */
        @Deprecated
        CROSS_PLATFORM(UIManager.getCrossPlatformLookAndFeelClassName());

        public final String className;

        Look(String className) {
            this.className = className;
        }
    }

    /**
     * 改变全部控件字体,慢?
     */
    public static void initGlobalFont(Font font) {
        FontUIResource fontRes = new FontUIResource(font);
        for (Enumeration<Object> keys = UIManager.getDefaults().keys(); keys.hasMoreElements(); ) {
            Object key = keys.nextElement();
            Object value = UIManager.get(key);
            if (value instanceof FontUIResource) {
                UIManager.put(key, fontRes);
            }
        }
    }

    /**
     * 改变常用控件字体
     */
    private static void initFontAndSize(Font font) {
        /*
        目前不能实现缩放的
            "JFormattedTextField", "JSpinner", "JComponent",
        优化方案
        JSpinner改用BaseNumSpinner
         */

        String[] names = {
                "JLabel", "JCheckBox", "JPopupMenu", "MenuItem", "JButton",
                "Label", "CheckBox", "PopupMenu", "MenuItem", "CheckBoxMenuItem",
                "JRadioButtonMenuItem", "ComboBox", "Button", "Tree", "ScrollPane",
                "TabbedPane", "EditorPane", "TitledBorder", "Menu", "TextArea",
                "OptionPane", "MenuBar", "ToolBar", "ToggleButton", "ToolTip",
                "ProgressBar", "TableHeader", "Panel", "List", "ColorChooser",
                "PasswordField", "TextField", "Table", "Label", "Viewport",
                "RadioButtonMenuItem", "RadioButton", "DesktopPane", "InternalFrame",
        };
        for (String item : names) {
            UIManager.put(item + ".font", font);
        }

        String key;
        //处理JSpinner的按钮大小;字体大小仍需在创建对象后调用修复方法
        key = "Spinner.arrowButtonSize";
        Dimension dimension = UIManager.getDimension(key);
        //只要更新宽度,高度是字体决定的;原生的太小了,还追加了额外的0.5倍
        dimension.width *= (screenScale + 0.5);
        UIManager.put(key, dimension);

        //测试打印
        //for (Entry<Object, Object> next : UIManager.getDefaults().entrySet()) {
        //    boolean contains = next.getKey().toString().contains("Spinner");
        //    if (contains) {
        //        log.info("{}", next.toString());
        //    }
        //}
    }

    public static void initFrameSize(Window window, int width, int height) {
        window.setSize((int) (width * screenScale), (int) (height * screenScale));
    }

    /**
     * @param c 一般控件调用前,先设置PreferredSize
     */
    public static void fixScale(Component c) {
        if (c instanceof JSpinner || c instanceof JFormattedTextField) {
            fixFontScale(c);
        } else {
            Dimension preferredSize = c.getPreferredSize();
            fixScaledSize(c, preferredSize.width, preferredSize.height);
        }
    }

    private static void fixScaledSize(Component c, int width, int height) {
        width = (int) (width * screenScale);
        height = (int) (height * screenScale);
        Dimension dim = new Dimension(width, height);
        c.setMinimumSize(dim);
        c.setPreferredSize(dim);
    }

    /**
     * 用于修改字体大小后,实现缩放功能
     */
    public static void fixFontScale(Component c) {
        Font old = c.getFont();
        Font font = new Font(old.getName(), old.getStyle(), (int) (old.getSize() * screenScale));
        c.setFont(font);
    }

    /**
     * 隐藏 {@link JSlider} 有焦点时的边框!
     *
     * @apiNote 是全局性的, 在app启动时或创建JSlider实例前调用; 一般按钮可以调用 {@link AbstractButton#setFocusPainted(boolean)}, 但{@link JSlider} 只能这样配置
     */
    public static void hideSliderFocusPaint() {
        UIManager.put("Slider.focus", new Color(0, 0, 0, 0));
    }

}
